//
// Ported from Mono
//

using System.Runtime.Serialization;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Threading;

using Rsdn.SmartApp.FW40;

namespace System
{
    /// <summary/>
    [SerializableAttribute]
    [ComVisibleAttribute(false)]
    [HostProtectionAttribute(
        SecurityAction.LinkDemand,
        Synchronization = true,
        ExternalThreading = true)]
    public class Lazy<T> : ISerializable
    {
        private T _value;
        private readonly LazyExecutionMode _mode;
        private readonly Func<T> _factory;
        private readonly object _monitor;

        /// <summary/>
        public Lazy()
            : this(LazyExecutionMode.NotThreadSafe)
        { }

        /// <summary/>
        public Lazy(Func<T> valueFactory)
            : this(valueFactory, LazyExecutionMode.NotThreadSafe)
        { }

        /// <summary/>
        public Lazy(LazyExecutionMode mode)
        {
            _mode = mode;
            if (mode != LazyExecutionMode.NotThreadSafe)
                _monitor = new Object();
        }

        /// <summary/>
        public Lazy(Func<T> valueFactory, LazyExecutionMode mode)
        {
            if (valueFactory == null)
                throw new ArgumentNullException("valueFactory");
            _factory = valueFactory;
            _mode = mode;
            if (mode != LazyExecutionMode.NotThreadSafe)
                _monitor = new Object();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Lazy&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="info">The info.</param>
        /// <param name="context">The context.</param>
        /// <exception cref="NotImplementedException"><c>NotImplementedException</c>.</exception>
        protected Lazy(SerializationInfo info, StreamingContext context)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object.
        /// </summary>
        /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data.</param>
        /// <param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization.</param>
        /// <exception cref="T:System.Security.SecurityException">
        /// The caller does not have the required permission.
        /// </exception>
        /// <exception cref="NotImplementedException"><c>NotImplementedException</c>.</exception>
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return !IsValueCreated ? FW40Resources.Lazy_ValueIsNotCreated : Value.ToString();
        }

        /// <summary/>
        public T Value
        {
            get
            {
                return IsValueCreated ? _value : InitValue();
            }
        }

        private T InitValue()
        {
            if (_mode == LazyExecutionMode.NotThreadSafe)
            {
                _value = CreateValue();
                IsValueCreated = true;
            }
            else
            {
                // We treat AllowMultipleThread... as EnsureSingleThread...
                lock (_monitor)
                {
                    if (IsValueCreated)
                        return _value;
                    var v = CreateValue();
                    _value = v;
                    Thread.MemoryBarrier();
                    IsValueCreated = true;
                }
            }

            return _value;
        }

        private T CreateValue()
        {
            if (_factory == null)
                try
                {
                    return Activator.CreateInstance<T>();
                }
                catch (MissingMethodException)
                {
                    throw new MissingMemberException(FW40Resources.Lazy_NoConstructor);
                }
            return _factory();
        }

        /// <summary/>
        public bool IsValueCreated { get; private set; }
    }
}